package main

import (
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
    "strings"
)

func main() {
    // 检查命令行参数
    args := os.Args[1:]
    if len(args) > 0 {
        switch args[0] {
        case "add-plugin":
            if len(args) < 2 {
                fmt.Println("Usage: app add-plugin <plugin-file>")
                os.Exit(1)
            }
            if err := addPlugin(args[1]); err != nil {
                fmt.Printf("Failed to add plugin: %v\n", err)
                os.Exit(1)
            }
            fmt.Printf("Plugin %s added successfully\n", args[1])
            return
        case "remove-plugin":
            if len(args) < 2 {
                fmt.Println("Usage: app remove-plugin <plugin-name>")
                os.Exit(1)
            }
            if err := removePlugin(args[1]); err != nil {
                fmt.Printf("Failed to remove plugin: %v\n", err)
                os.Exit(1)
            }
            fmt.Printf("Plugin %s removed successfully\n", args[1])
            return
        case "list-plugins":
            if err := listPlugins(); err != nil {
                fmt.Printf("Failed to list plugins: %v\n", err)
                os.Exit(1)
            }
            return
        }
    }
    
    fmt.Println("XJar Plus Application Launcher")
    
    // 获取当前可执行文件的目录
    exePath, _ := os.Executable()
    exeDir := filepath.Dir(exePath)
    
    // 检查app.x是否存在
    appPath := filepath.Join(exeDir, "app.x")
    if _, err := os.Stat(appPath); os.IsNotExist(err) {
        fmt.Printf("Error: app.x not found at %s\n", appPath)
        os.Exit(1)
    }
    
    // 提取插件
    pluginDir := filepath.Join(exeDir, "plugins")
    if err := extractPlugins(pluginDir); err != nil {
        fmt.Printf("Warning: Failed to extract plugins: %v\n", err)
    }
    
    // 将插件中的类文件合并到基座项目中
    if err := mergePluginsToBaseInPlace(appPath, pluginDir); err != nil {
        fmt.Printf("Warning: Failed to merge plugins: %v\n", err)
    } else {
        fmt.Println("Plugins merged successfully into base application")
    }
    
    // 运行Java命令启动应用
    argsStr := "-jar " + appPath
    cmdArgs := strings.Fields(argsStr)
    cmd := exec.Command("java", cmdArgs...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    
    fmt.Println("Starting application...")
    if err := cmd.Run(); err != nil {
        fmt.Printf("Error running application: %v\n", err)
        os.Exit(1)
    }
}

// addPlugin 添加插件
func addPlugin(pluginFile string) error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")
    
    // 创建插件目录（如果不存在）
    if err := os.MkdirAll(pluginsDir, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create plugins directory: %v", err)
    }
    
    // 检查插件文件是否存在
    if _, err := os.Stat(pluginFile); os.IsNotExist(err) {
        return fmt.Errorf("plugin file does not exist: %s", pluginFile)
    }
    
    // 获取插件文件名
    pluginName := filepath.Base(pluginFile)
    
    // 检查是否为.x文件
    if filepath.Ext(pluginName) != ".x" {
        return fmt.Errorf("plugin file must have .x extension")
    }
    
    // 复制插件文件到插件目录
    pluginPath := filepath.Join(pluginsDir, pluginName)
    
    // 读取插件文件
    data, err := os.ReadFile(pluginFile)
    if err != nil {
        return fmt.Errorf("failed to read plugin file: %v", err)
    }
    
    // 写入插件文件
    if err := os.WriteFile(pluginPath, data, os.ModePerm); err != nil {
        return fmt.Errorf("failed to write plugin file: %v", err)
    }
    
    return nil
}

// removePlugin 移除插件
func removePlugin(pluginName string) error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")
    
    // 构建插件文件路径
    pluginPath := filepath.Join(pluginsDir, pluginName)
    
    // 检查插件文件是否存在
    if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
        return fmt.Errorf("plugin does not exist: %s", pluginName)
    }
    
    // 删除插件文件
    if err := os.Remove(pluginPath); err != nil {
        return fmt.Errorf("failed to remove plugin: %v", err)
    }
    
    return nil
}

// listPlugins 列出所有插件
func listPlugins() error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")
    
    // 检查插件目录是否存在
    if _, err := os.Stat(pluginsDir); os.IsNotExist(err) {
        fmt.Println("No plugins directory found")
        return nil
    }
    
    // 读取插件目录
    entries, err := os.ReadDir(pluginsDir)
    if err != nil {
        return fmt.Errorf("failed to read plugins directory: %v", err)
    }
    
    fmt.Println("Installed plugins:")
    for _, entry := range entries {
        if entry.IsDir() {
            continue
        }
        
        // 只显示.x文件
        if filepath.Ext(entry.Name()) == ".x" {
            info, _ := entry.Info()
            fmt.Printf("- %s (size: %d bytes)\n", entry.Name(), info.Size())
        }
    }
    
    return nil
}

// extractPlugins 提取嵌入的插件文件到指定目录
func extractPlugins(pluginPath string) error {
    fmt.Printf("Extracting plugins to: %s\n", pluginPath)

    // 创建插件目录
    if err := os.MkdirAll(pluginPath, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create plugin directory: %v", err)
    }

    // 检查是否存在嵌入的插件文件
    embeddedPluginsDir := filepath.Join(filepath.Dir(os.Args[0]), "plugins")

    // 如果嵌入的插件目录存在，则复制插件文件
    if _, err := os.Stat(embeddedPluginsDir); err == nil {
        entries, err := os.ReadDir(embeddedPluginsDir)
        if err != nil {
            fmt.Printf("Warning: Failed to read embedded plugins directory: %v\n", err)
            return nil
        }

        extractedCount := 0
        for _, entry := range entries {
            if entry.IsDir() {
                continue
            }

            // 只处理.x文件
            if filepath.Ext(entry.Name()) == ".x" {
                srcPath := filepath.Join(embeddedPluginsDir, entry.Name())
                dstPath := filepath.Join(pluginPath, entry.Name())

                if err := copyFile(srcPath, dstPath); err != nil {
                    fmt.Printf("Warning: Failed to copy plugin %s: %v\n", entry.Name(), err)
                    continue
                }

                fmt.Printf("Extracted plugin: %s\n", entry.Name())
                extractedCount++
            }
        }

        fmt.Printf("Extracted %d plugin(s)\n", extractedCount)
    } else {
        fmt.Println("No embedded plugins found")
    }

    fmt.Println("Plugin extraction complete")
    return nil
}

// pathExists 检查路径是否存在
func pathExists(path string) (bool, error) {
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return false, nil
    } else if err != nil {
        return false, err
    }
    return true, nil
}

// validatePlugin 验证插件文件的基本结构
func validatePlugin(pluginPath string) error {
    // 检查文件是否存在
    if exists, _ := pathExists(pluginPath); !exists {
        return fmt.Errorf("plugin file does not exist: %s", pluginPath)
    }

    // 创建临时目录用于快速验证
    tempDir, err := os.MkdirTemp("", "plugin-validate")
    if err != nil {
        return fmt.Errorf("failed to create temp directory for validation: %v", err)
    }
    defer os.RemoveAll(tempDir)

    // 尝试解压插件以验证其结构
    if err := unzip(pluginPath, tempDir); err != nil {
        return fmt.Errorf("invalid plugin format: %v", err)
    }

    return nil
}

// mergePluginsToBaseInPlace 直接将插件中的类文件追加到基座项目中
func mergePluginsToBaseInPlace(baseAppPath, pluginDir string) error {
    fmt.Printf("Merging plugins from %s into base application %s in-place\n", pluginDir, baseAppPath)

    // 检查基座应用是否存在
    if _, err := os.Stat(baseAppPath); os.IsNotExist(err) {
        return fmt.Errorf("base application not found: %s", baseAppPath)
    }

    // 检查插件目录是否存在
    if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
        fmt.Println("No plugin directory found, skipping merge")
        return nil
    }

    // 读取插件目录
    entries, err := os.ReadDir(pluginDir)
    if err != nil {
        return fmt.Errorf("failed to read plugin directory: %v", err)
    }

    // 检查是否有插件
    pluginCount := 0
    for _, entry := range entries {
        if !entry.IsDir() && filepath.Ext(entry.Name()) == ".x" {
            pluginCount++
        }
    }

    if pluginCount == 0 {
        fmt.Println("No plugins found, skipping merge")
        return nil
    }

    fmt.Printf("Found %d plugin(s) to merge\n", pluginCount)

    // 处理每个插件，直接将插件内容追加到基座项目中
    for _, entry := range entries {
        if !entry.IsDir() && filepath.Ext(entry.Name()) == ".x" {
            pluginPath := filepath.Join(pluginDir, entry.Name())
            fmt.Printf("Processing plugin: %s\n", entry.Name())

            // 验证插件文件存在且可读
            if err := validatePlugin(pluginPath); err != nil {
                fmt.Printf("Warning: failed to validate plugin %s: %v\n", entry.Name(), err)
                continue
            }

            // 将插件内容直接追加到基座项目中
            if err := appendPluginToBase(pluginPath, baseAppPath); err != nil {
                fmt.Printf("Warning: failed to append plugin %s: %v\n", entry.Name(), err)
                continue
            }

            fmt.Printf("Plugin %s appended successfully\n", entry.Name())
        }
    }

    fmt.Println("Plugin merge completed in-place")
    return nil
}

// appendPluginToBase 将插件中的.class文件复制到基座app.x项目中BOOT-INF/classes
func appendPluginToBase(pluginPath, baseAppPath string) error {
    // 创建临时目录用于解压插件
    tempDir, err := os.MkdirTemp("", "plugin-extract")
    if err != nil {
        return fmt.Errorf("failed to create temp directory: %v", err)
    }
    defer os.RemoveAll(tempDir)

    // 解压插件到临时目录
    if err := unzip(pluginPath, tempDir); err != nil {
        return fmt.Errorf("failed to unzip plugin: %v", err)
    }

    // 创建临时目录用于存放要添加到基座项目的.class文件
    tempClassesDir := filepath.Join(tempDir, "")
    if err := os.MkdirAll(tempClassesDir, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create temp classes directory: %v", err)
    }

    // 直接从插件根目录开始扫描所有.class文件
    err = filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // 跳过META-INF目录及其内容
        if strings.Contains(path, "META-INF") {
            if info.IsDir() && filepath.Base(path) == "META-INF" {
                return filepath.SkipDir
            }
            return nil
        }

        // 只处理.class文件
        if !info.IsDir() && filepath.Ext(path) == ".class" {
            // 计算相对于tempDir的路径
            relPath, err := filepath.Rel(tempDir, path)
            if err != nil {
                return err
            }

            // 构建目标路径（确保在BOOT-INF/classes下）
            targetPath := filepath.Join(tempClassesDir, relPath)

            // 确保目标目录存在
            targetDir := filepath.Dir(targetPath)
            if err := os.MkdirAll(targetDir, os.ModePerm); err != nil {
                return err
            }

            // 复制.class文件
            if err := copyFile(path, targetPath); err != nil {
                return fmt.Errorf("failed to copy class file %s: %v", relPath, err)
            }
            
            fmt.Printf("Copied class file: %s\n", relPath)
        }

        return nil
    })

    if err != nil {
        return fmt.Errorf("failed to walk through plugin: %v", err)
    }

    // 检查是否有.class文件被复制
    hasClasses := false
    filepath.Walk(tempClassesDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if !info.IsDir() && filepath.Ext(path) == ".class" {
            hasClasses = true
            return filepath.SkipDir // 找到一个就足够了
        }
        return nil
    })

    if !hasClasses {
        fmt.Printf("No .class files found in plugin %s, skipping\n", filepath.Base(pluginPath))
        return nil
    }

    // 使用jar命令将临时目录中的.class文件追加到基座项目中
    cmd := exec.Command("jar", "uf0", baseAppPath, "-C", filepath.Join(tempDir, ""), ".")
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("failed to append plugin classes to base app: %v, output: %s", err, string(output))
    }

    fmt.Printf("Successfully appended .class files from plugin %s to base app\n", filepath.Base(pluginPath))
    return nil
}

// unzip 解压ZIP文件到指定目录
func unzip(src, dest string) error {
    cmd := exec.Command("unzip", "-q", src, "-d", dest)
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("failed to unzip: %v, output: %s", err, string(output))
    }
    return nil
}

// copyDir 复制目录
func copyDir(src, dst string) error {
    entries, err := os.ReadDir(src)
    if err != nil {
        return err
    }

    if err := os.MkdirAll(dst, os.ModePerm); err != nil {
        return err
    }

    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())

        if entry.IsDir() {
            if err := copyDir(srcPath, dstPath); err != nil {
                return err
            }
        } else {
            if err := copyFile(srcPath, dstPath); err != nil {
                return err
            }
        }
    }

    return nil
}

// copyFile 复制文件
func copyFile(src, dst string) error {
    data, err := os.ReadFile(src)
    if err != nil {
        return err
    }

    // 确保目标目录存在
    dstDir := filepath.Dir(dst)
    if err := os.MkdirAll(dstDir, os.ModePerm); err != nil {
        return err
    }

    return os.WriteFile(dst, data, os.ModePerm)
}
